using System;
using System.Collections;

namespace Server.Engines.Reports
{
	public enum BarGraphRenderMode
	{
		Bars,
		Lines
	}

	public class BarGraph : Chart
	{
		#region Type Identification
		public static readonly PersistableType ThisTypeID = new PersistableType( "bg", new ConstructCallback( Construct ) );

		private static PersistableObject Construct()
		{
			return new BarGraph();
		}

		public override PersistableType TypeID{ get{ return ThisTypeID; } }
		#endregion

		private int m_Ticks;
		private BarGraphRenderMode m_RenderMode;

		private string m_xTitle;
		private string m_yTitle;

		private int m_FontSize = 7;
		private int m_Interval = 1;

		private BarRegion[] m_Regions;

		public int Ticks{ get{ return m_Ticks; } set{ m_Ticks = value; } }
		public BarGraphRenderMode RenderMode{ get{ return m_RenderMode; } set{ m_RenderMode = value; } }

		public string xTitle{ get{ return m_xTitle; } set{ m_xTitle = value; } }
		public string yTitle{ get{ return m_yTitle; } set{ m_yTitle = value; } }

		public int FontSize{ get{ return m_FontSize; } set{ m_FontSize = value; } }
		public int Interval{ get{ return m_Interval; } set{ m_Interval = value; } }

		public BarRegion[] Regions{ get{ return m_Regions; } set{ m_Regions = value; } }

		public BarGraph( string name, string fileName, int ticks, string xTitle, string yTitle, BarGraphRenderMode rm )
		{
			m_Name = name;
			m_FileName = fileName;
			m_Ticks = ticks;
			m_xTitle = xTitle;
			m_yTitle = yTitle;
			m_RenderMode = rm;
		}

		private BarGraph()
		{
		}

		public override void SerializeAttributes( PersistanceWriter op )
		{
			base.SerializeAttributes( op );

			op.SetInt32( "t", m_Ticks );
			op.SetInt32( "r", (int) m_RenderMode );

			op.SetString( "x", m_xTitle );
			op.SetString( "y", m_yTitle );

			op.SetInt32( "s", m_FontSize );
			op.SetInt32( "i", m_Interval );
		}

		public override void DeserializeAttributes( PersistanceReader ip )
		{
			base.DeserializeAttributes( ip );

			m_Ticks = ip.GetInt32( "t" );
			m_RenderMode = (BarGraphRenderMode) ip.GetInt32( "r" );

			m_xTitle = Utility.Intern( ip.GetString( "x" ) );
			m_yTitle = Utility.Intern( ip.GetString( "y" ) );

			m_FontSize = ip.GetInt32( "s" );
			m_Interval = ip.GetInt32( "i" );
		}

		public static int LookupReportValue( Snapshot ss, string reportName, string valueName )
		{
			for ( int j = 0; j < ss.Children.Count; ++j )
			{
				Report report = ss.Children[j] as Report;

				if ( report == null || report.Name != reportName )
					continue;

				for ( int k = 0; k < report.Items.Count; ++k )
				{
					ReportItem item = report.Items[k];

					if ( item.Values[0].Value == valueName )
						return Utility.ToInt32( item.Values[1].Value );
				}

				break;
			}

			return -1;
		}

		public static BarGraph DailyAverage( SnapshotHistory history, string reportName, string valueName )
		{
			int[] totals = new int[24];
			int[] counts = new int[24];

			int min = history.Snapshots.Count - (7 * 24); // averages over one week

			if ( min < 0 )
				min = 0;

			for ( int i = min; i < history.Snapshots.Count; ++i )
			{
				Snapshot ss = history.Snapshots[i];

				int val = LookupReportValue( ss, reportName, valueName );

				if ( val == -1 )
					continue;

				int hour = ss.TimeStamp.TimeOfDay.Hours;

				totals[hour] += val;
				counts[hour]++;
			}

			BarGraph barGraph = new BarGraph( "Hourly average " + valueName, "graphs_" + valueName.ToLower() + "_avg", 10, "Time", valueName, BarGraphRenderMode.Lines );

			barGraph.m_FontSize = 6;

			for ( int i = 7; i <= totals.Length+7; ++i )
			{
				int val;

				if ( counts[i%totals.Length] == 0 )
					val = 0;
				else
					val = (totals[i%totals.Length] + (counts[i%totals.Length] / 2)) / counts[i%totals.Length];

				int realHours = i%totals.Length;
				int hours;

				if ( realHours == 0 )
					hours = 12;
				else if ( realHours > 12 )
					hours = realHours - 12;
				else
					hours = realHours;

				barGraph.Items.Add( hours + (realHours >= 12 ? " PM" : " AM"), val );
			}

			return barGraph;
		}

		public static BarGraph Growth( SnapshotHistory history, string reportName, string valueName )
		{
			BarGraph barGraph = new BarGraph( "Growth of " + valueName + " over time", "graphs_" + valueName.ToLower() + "_growth", 10, "Time", valueName, BarGraphRenderMode.Lines );

			barGraph.FontSize = 6;
			barGraph.Interval = 7;

			DateTime startPeriod = history.Snapshots[0].TimeStamp.Date + TimeSpan.FromDays( 1.0 );
			DateTime endPeriod = history.Snapshots[history.Snapshots.Count - 1].TimeStamp.Date;

			ArrayList regions = new ArrayList();

			DateTime curDate = DateTime.MinValue;
			int curPeak = -1;
			int curLow  = 1000;
			int curTotl = 0;
			int curCont = 0;
			int curValu = 0;

			for ( int i = 0; i < history.Snapshots.Count; ++i )
			{
				Snapshot ss = history.Snapshots[i];
				DateTime timeStamp = ss.TimeStamp;

				if ( timeStamp < startPeriod || timeStamp >= endPeriod )
					continue;

				int val = LookupReportValue( ss, reportName, valueName );

				if ( val == -1 )
					continue;

				DateTime thisDate = timeStamp.Date;

				if ( curDate == DateTime.MinValue )
					curDate = thisDate;

				curCont++;
				curTotl += val;
				curValu = curTotl / curCont;

				if ( curDate != thisDate && curValu >= 0 )
				{
					string mnthName = thisDate.ToString( "MMMM" );

					if ( regions.Count == 0 )
					{
						regions.Add( new BarRegion( barGraph.Items.Count, barGraph.Items.Count, mnthName ) );
					}
					else
					{
						BarRegion region = (BarRegion)regions[regions.Count - 1];

						if ( region.m_Name == mnthName )
							region.m_RangeTo = barGraph.Items.Count;
						else
							regions.Add( new BarRegion( barGraph.Items.Count, barGraph.Items.Count, mnthName ) );
					}

					barGraph.Items.Add( thisDate.Day.ToString(), curValu );

					curPeak = val;
					curLow = val;
				}
				else
				{
					if ( val > curPeak )
						curPeak = val;

					if ( val > 0 && val < curLow )
						curLow = val;
				}

				curDate = thisDate;
			}

			barGraph.Regions = (BarRegion[])regions.ToArray( typeof( BarRegion ) );

			return barGraph;
		}

		public static BarGraph OverTime( SnapshotHistory history, string reportName, string valueName, int step, int max, int ival )
		{
			BarGraph barGraph = new BarGraph( valueName + " over time", "graphs_" + valueName.ToLower() + "_ot", 10, "Time", valueName, BarGraphRenderMode.Lines );

			TimeSpan ts = TimeSpan.FromHours( (max*step)-0.5 );

			DateTime mostRecent = history.Snapshots[history.Snapshots.Count - 1].TimeStamp;
			DateTime minTime = mostRecent - ts;

			barGraph.FontSize = 6;
			barGraph.Interval = ival;

			ArrayList regions = new ArrayList();

			for ( int i = 0; i < history.Snapshots.Count; ++i )
			{
				Snapshot ss = history.Snapshots[i];
				DateTime timeStamp = ss.TimeStamp;

				if ( timeStamp < minTime )
					continue;

				if ( (i % step) != 0 )
					continue;

				int val = LookupReportValue( ss, reportName, valueName );

				if ( val == -1 )
					continue;

				int realHours = timeStamp.TimeOfDay.Hours;
				int hours;

				if ( realHours == 0 )
					hours = 12;
				else if ( realHours > 12 )
					hours = realHours - 12;
				else
					hours = realHours;

				string dayName = timeStamp.DayOfWeek.ToString();

				if ( regions.Count == 0 )
				{
					regions.Add( new BarRegion( barGraph.Items.Count, barGraph.Items.Count, dayName ) );
				}
				else
				{
					BarRegion region = (BarRegion) regions[regions.Count - 1];

					if ( region.m_Name == dayName )
						region.m_RangeTo = barGraph.Items.Count;
					else
						regions.Add( new BarRegion( barGraph.Items.Count, barGraph.Items.Count, dayName ) );
				}

				barGraph.Items.Add( hours + (realHours >= 12 ? " PM" : " AM"), val );
			}

			barGraph.Regions = (BarRegion[])regions.ToArray( typeof( BarRegion ) );

			return barGraph;
		}
	}
}